#include "vtkMeshExport.hpp"
#include "vtkMeshImport.hpp"
#include "vtkFieldExport_constant.hpp"
#include "vtkFieldImport_constant.hpp"
#include "sweSolver.hpp"

#include "meshTypeFunctions.hpp"

#include <catch2/catch_test_macros.hpp>

#include <cmath>

using namespace hms;
using namespace hms::vtk;
namespace xml = tinyxml2;

#define REQUIRE_MANNING(REQUIREMENT) \
readConstantFields(constDoc.get(), solver); \
std::visit( [&](const auto& field){REQUIRE(REQUIREMENT);}, solver.n() )

TEST_CASE("Importing constant field"){
	auto test = []( auto&& meshDummy){
		using MeshType = remove_qualifiers<decltype(meshDummy)>;
		scalar dx {1.3}, dy {0.8};

		MeshVariant meshVar { uniMeshAs<MeshType>(3,4,dx,dy) };

		const MeshType& mesh { std::get<MeshType>(meshVar) };

		Time time {10};
		time.writeTimeInterval(1);

		SWESolver<MeshType> solver {mesh, time};

		std::unique_ptr<xml::XMLDocument> constDoc {
			constantFieldsToXML(solver)
		};

		xml::XMLElement* constFields {
			getChildNodeByName( constDoc.get(), constNode )
		};
		xml::XMLElement* manning { getChildNodeByAttributeValue(constFields,
			constChild, "Name", "ManningsN"
		) };

		SECTION("Uniform, init by value"){
			/* one component */
			manning->SetText(42);
			REQUIRE_MANNING( field.storage().isApprox( Array1s{42} ) );

			/* two components */
			manning->SetAttribute(nComponentsName, 2);
			manning->SetText("0.2 0.3");
			REQUIRE_MANNING( field.storage().isApprox( Array2s{0.2, 0.3} ) );
		}
		SECTION("Non-uniform, init by value"){
			/* one component */
			manning->SetAttribute("isUniform", false);
			ArrayXXs testVals (1, 12);
			testVals << 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11;
			matrixToText(manning, testVals);

			REQUIRE_MANNING( field.domainValues().isApprox(testVals) );

			/* two components */
			manning->SetAttribute(nComponentsName, 2);
			matrixToText(manning, testVals.replicate(2,1));
			REQUIRE_MANNING( field.domainValues().isApprox( testVals.replicate(2,1) ) );
		}
		SECTION("Uniform, init by function"){
			/* one component */
			manning->SetAttribute("isFunction", true);
			writeFunction(manning, "g", 0);
			REQUIRE_MANNING( field.storage().isApprox( Array1s{phys::g} ) );

			/* two components */
			manning->SetAttribute(nComponentsName, 2);
			writeFunction(manning, "pi", 1);
			REQUIRE_MANNING( field.storage().isApprox(Array2s{phys::g, math::pi}) );
		}
		SECTION("Non-uniform, init by function"){
			/* one component */
			manning->SetAttribute("isFunction", true);
			manning->SetAttribute("isUniform", false);
			writeFunction(manning, "x+y", 0);
			ArrayXXs testVals0 (1,12);
			testVals0 = (
				(dx * Array3s{0.5, 1.5, 2.5}).replicate(1,4) +
				(dy * RowArray4s{0.5, 1.5, 2.5, 3.5}).replicate(3,1)
			).reshaped(1,12);
			REQUIRE_MANNING( field.domainValues().isApprox(testVals0) );

			/* two components */
			manning->SetAttribute(nComponentsName, 2);
			writeFunction(manning, "z", 1);
			ArrayXXs testVals1 (2,12);
			testVals1.row(0) = testVals0;
			testVals1.row(1) = solver.z().domainValues();
			REQUIRE_MANNING( field.domainValues().isApprox(testVals1) );
		}
	};

	SECTION("Mesh"){
		test( Mesh{} );
	}
	SECTION("UniMesh"){
		test( UniMesh{} );
	}
	SECTION("RectMesh"){
		test( RectMesh{} );
	}
	SECTION("StructMesh"){
		test( StructMesh{} );
	}
}

#undef REQUIRE_MANNING